package fpeas.util;

import fpeas.function.Function;
import static fpeas.function.FunctionUtility.identity;
import fpeas.maybe.Maybe;
import static fpeas.maybe.MaybeUtility.fromNullable;
import fpeas.pair.Pair;
import static fpeas.pair.PairUtility.pair;

import java.util.Iterator;
import java.util.NoSuchElementException;

public class Collections
{
	private static final Function<Pair<Integer,Integer>,Integer> min=new Function<Pair<Integer,Integer>,Integer>()
	{
		public Integer run(final Pair<Integer, Integer> pair)
		{
			return Math.min(pair.first(),pair.second());
		}
	};

	public static <T,R> R forEach(final Iterable<T> iterable, final Function<T,R> function, final Function<Pair<R,R>,R> operator, final R reserve)
	{
		R result=reserve;
		boolean first=true;

		for (final T t: iterable)
		{
			if (first)
				result=function.run(t);
			else
				result=operator.run(pair(result,function.run(t)));

			first=false;
		}

		return result;
	}

	public static Maybe<Integer> min(final Iterable<Integer> iterable)
	{
		final Function<Integer,Integer> identity=identity();
		return fromNullable(forEach(iterable,identity,min,null));
	}

	public static <T> Iterable<T> filter(final Iterable<T> iterable,final Function<T,Boolean> condition)
	{
		return new Iterable<T>()
		{
			public Iterator<T> iterator()
			{
				final Iterator<T> wrapped=iterable.iterator();

				return new Iterator<T>()
				{
					private T next;

					public boolean hasNext()
					{
						if (next!=null)
							return true;

						while (wrapped.hasNext())
						{
							final T possibleNext=wrapped.next();
							if (condition.run(possibleNext))
							{
								next=possibleNext;
								return true;
							}
						}

						return false;
					}

					public T next()
					{
						if (next!=null)
						{
							final T tempNext=next;
							next=null;
							return tempNext;
						}

						while (wrapped.hasNext())
						{
							final T possibleNext=wrapped.next();
							if (condition.run(possibleNext))
								return possibleNext;
						}

						throw new NoSuchElementException();
					}

					public void remove()
					{
						throw new UnsupportedOperationException();
					}
				};
			}
		};
	}
}